package org.urchr.beorganized.api.base;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.urchr.beorganized.dao.NotesDAO;
import org.urchr.beorganized.dao.XMLDocumentBuilder;
import org.urchr.beorganized.dao.errors.GeneralException;
import org.urchr.beorganized.dao.errors.InvalidFileException;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

abstract public class BaseAPIServlet {

	protected HttpServletRequest request = null;
	protected HttpServletResponse response = null;
	protected boolean formatJSON = false;
	protected String jSONCallback = null;
	protected Element xmlObject = null;
	protected boolean skipBody = false;
	protected String customContentType = null;
	protected ServletContext servletContext = null;
		
	protected boolean fileUpload = false;	
	protected List<FileItem> multipartItems = new ArrayList<FileItem>();
	
	protected FileItem getFile(String name){
		if(fileUpload){
			for (FileItem item : multipartItems) {
				if(name.equals(item.getFieldName()) && !item.isFormField()){
					return item;
				}
			}
		}
		return null;
	}
	
	protected String getParameter(String name){
		if(fileUpload){
			for (FileItem item : multipartItems) {
				if(name.equalsIgnoreCase(item.getFieldName()) && item.isFormField()){
					String result = item.getString();
					if(null!=result){
						try {
							result = new String(result.getBytes("latin1"), "utf-8");
						} catch (UnsupportedEncodingException e) {
						}
					}
					return result;
				}
			}
		}else
			return request.getParameter(name);
		return null;
	}
	
	public void doProcessRequestInternal(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws ServletException{
		this.servletContext = servletContext;
		this.request = request;
		this.response = response;
		try {
			request.setCharacterEncoding("utf-8");
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		fileUpload = ServletFileUpload.isMultipartContent(request);
		if(fileUpload){
			try {
				DiskFileItemFactory factory = new DiskFileItemFactory();				
				factory.setSizeThreshold(NotesDAO.FILES_MAX_SIZE);//Megabyte
				
				ServletFileUpload upload = new ServletFileUpload(factory);
//				upload.setSizeMax(NotesDAO.FILES_MAX_SIZE);
				multipartItems = upload.parseRequest(request);				
			} catch (Exception e) {
				fileUpload = false;
//				e.printStackTrace();
//				System.out.println("Sending file error");
//				XMLDocumentBuilder doc = new XMLDocumentBuilder();
//				doc.addText(doc.getRootElement("error"), new InvalidFileException().getCode());
//				sendNode(doc.getRootElement());
//				return;
			}
		}
		
		if("true".equals(getParameter("json"))){
			formatJSON = true;
			jSONCallback = getParameter("callback");
		}
		if(!skipBody){
			if(formatJSON){
				if(getParameter("object")!=null){
					try {
//						System.out.println("Parsing "+request.getParameter("object"));
						JSONObject object = new JSONObject(request.getParameter("object"));
						if(object.keys().hasNext()){
							String name = object.keys().next().toString();
							XMLDocumentBuilder doc = new XMLDocumentBuilder();
							doc.getRootElement(name);
							JSONObject anObject = object.getJSONObject(name);
							Iterator it = anObject.keys();
							while(it.hasNext()){
								String key = it.next().toString();
								String value = anObject.getString(key);
								if(value!=null){
									value = new String(value.getBytes("latin1"), "utf-8");
									doc.getRootElement().setAttribute(key, value);								
								}
							}
							xmlObject = doc.getRootElement();
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}else{
				XMLDocumentBuilder doc = new XMLDocumentBuilder();
				try {
					if(doc.readXML(request.getInputStream()))
						xmlObject = doc.getRootElement();
				} catch (Exception e) {
					// TODO Auto-generated catch block
//					e.printStackTrace();
				}
			}
		}
		try {
			doProcess();
		} catch (GeneralException e) {
			e.printStackTrace();
			//Create ERROR response
			XMLDocumentBuilder doc = new XMLDocumentBuilder();
			doc.addText(doc.getRootElement("error"), e.getCode());
			sendNode(doc.getRootElement());
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}
	
	protected Object nodeTojSON(Element node){
		try {
			Object result = "";
			JSONObject obj = new JSONObject();
			if(node.getAttributes().getLength()>0){
				for (int i = 0; i < node.getAttributes().getLength(); i++) {
					Attr attr = (Attr) node.getAttributes().item(i);
					obj.put(attr.getName(), attr.getValue());
				}
				result = obj;
//				return obj;
			}
			obj.put("nodeName", node.getTagName());
			NodeList nl = node.getChildNodes();
			if(nl.getLength()==0)
				return result;
			if(nl.item(0) instanceof Text)
				return ((Text)nl.item(0)).getTextContent();
			JSONArray arr = new JSONArray();
			obj.put("children", arr);
			for (int i = 0; i < nl.getLength(); i++) {
				if(nl.item(i) instanceof Element){
					arr.put(nodeTojSON((Element) nl.item(i)));
				}
			}
			return obj;
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	protected void sendNode(Element node){
		response.setCharacterEncoding("utf-8");
		if(!formatJSON){
			response.setContentType("text/xml");
			try {
				XMLDocumentBuilder.writeXML(node, response.getOutputStream(), false);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else{
			JSONObject object = new JSONObject();
			try {
				object.put(node.getNodeName(), nodeTojSON(node));
				object.put("success", "true");
				if(jSONCallback!=null){
					response.setContentType("text/javascript");
				}else{
					response.setContentType("text/html");
				}
				Writer writer = response.getWriter();
				if(jSONCallback!=null){
					writer.write(jSONCallback+"(");
				}
				writer.write(object.toString());
//				System.out.println("Response: "+object.toString());
				if(jSONCallback!=null){
					writer.write(");");
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	abstract protected void doProcess() throws GeneralException;
}
